﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using LibGreen.Protocol.IRC.Events;
using LibGreen.Protocol.IRC.Utility;
using LibGreen.Utility;



namespace LibGreen.Protocol.IRC
{
	internal class Connection
	{
		// this is CR+LF
		public readonly byte[] PacketTerminator = { 0x0D, 0x0A };

		#region int Port
		private int port;
		internal int Port
		{
			get { return port; }
			set { port = value; }
		}
		#endregion

		#region string Server
		private string server;

		internal string Server
		{
			get { return server; }
			set { server = value; }
		}
		#endregion

		#region MessagesToBeSent
		private Queue<ClientCommand> messagesToBeSent = new Queue<ClientCommand> ();

		internal void SendMessage(ClientCommand mess)
		{
			lock (messagesToBeSent) {
				messagesToBeSent.Enqueue (mess);
			}
		}
		#endregion

		#region MessagesRecieved
		private List<ServerMessage> messagesRecieved = new List<ServerMessage>();
		private EventWaitHandle packetRecieved = new EventWaitHandle (false, EventResetMode.AutoReset);
		
		/// <summary>
		/// Return all messages recieved.  Block until messages recieved.
		/// </summary>
		/// <returns></returns>
		internal ServerMessage[] RecieveMessage ()
		{
			if(messagesRecieved.Count==0)
				packetRecieved.WaitOne ();
			lock (messagesRecieved) {
				var messages = messagesRecieved.ToArray();
				messagesRecieved.RemoveRange (0, messagesRecieved.Count);
				return messages;
			}
		}

		private void PacketRecieved (byte[] packet)
		{
			lock (messagesRecieved) {
				messagesRecieved.Add (new ServerMessage (packet));
				packetRecieved.Set ();
			}
		}
		#endregion

		#region Socket
		private Socket soc;
		private Thread socketProcess;
		#endregion

		internal Connection (string server, int port)
		{
			Port = port;
			Server = server;
		}

		internal void Initialize ()
		{
			LibGreen.Utility.SocketFactory.Connect (Server, port,
					new ConnectionCreatedHandler (ConnectionReady));
		}

		internal void ProcessPacket ()
		{
			int location = 0;
			int bytesRead = 0;
			int packetEnd;
			byte[] buf = new byte[512];
			
			while (true) {
				bytesRead = soc.Receive (buf, location,512-location, SocketFlags.None);
				packetEnd = buf.FindIndex (location, bytesRead, val => val == PacketTerminator[1]);
				location += bytesRead;
				if (packetEnd != -1 && buf[packetEnd - 1] == PacketTerminator[0]) {
					byte[] newPacket = new byte[packetEnd + 1];
					Array.Copy (buf, newPacket, packetEnd + 1);
					this.PacketRecieved (newPacket);
					Array.Copy(buf, packetEnd + 1, buf, 0, (location - 1) - packetEnd);
					
					location = 0;
				}
				if (messagesToBeSent.Count != 0) {
					lock (messagesToBeSent) {
						foreach (ClientCommand mes in messagesToBeSent) {
							soc.Send (mes.Packet);
						}
					}
				}
			}
		}

		
		internal event ReadyForLoginHandler ReadyForLogin;
		
		private void ConnectionReady (Socket soc, string error)
		{
			if (error != null || error != "")
				throw new Excepetions.LibGreenException ();
			socketProcess = new Thread (new ThreadStart (ProcessPacket));
			ReadyForLogin (this);
		}
	}
}
